package com.atguigu.yygh.hosp.service.impl;

import com.atguigu.yygh.Exception.YyghException;
import com.atguigu.yygh.hosp.repository.ScheduleRepository;
import com.atguigu.yygh.hosp.service.DepartmentService;
import com.atguigu.yygh.hosp.service.HospitalService;
import com.atguigu.yygh.hosp.service.ScheduleService;
import com.atguigu.yygh.model.hosp.Schedule;
import com.atguigu.yygh.vo.hosp.BookingScheduleRuleVo;
import com.atguigu.yygh.vo.hosp.ScheduleQueryVo;
import org.joda.time.DateTime;
import org.joda.time.DateTimeConstants;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.*;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.aggregation.Aggregation;
import org.springframework.data.mongodb.core.aggregation.AggregationResults;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.stereotype.Service;

import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Service
public class ScheduleServiceImpl implements ScheduleService {
    @Autowired
    ScheduleRepository scheduleRepository;
    @Autowired
    MongoTemplate mongoTemplate;
    @Autowired
    HospitalService hospitalService;
    @Autowired
    DepartmentService departmentService;
    //上传排班信息
    @Override
    public void save(Schedule schedule) {
        if (schedule==null){//如果schedule为空,直接抛出异常
            throw new YyghException(20001,"参数错误!");
        }
        //先从数据查询该条数据
        Schedule findSchedule = scheduleRepository.getScheduleByHoscodeAndHosScheduleId(schedule.getHoscode(),schedule.getHosScheduleId());
        if (findSchedule==null){//如果为null 进行添加
            schedule.setUpdateTime(new Date());//设置更新时间
            schedule.setCreateTime(new Date());//设置创建时间时间
            schedule.setIsDeleted(0);//设置是否逻辑删除
            scheduleRepository.save(schedule);
        }else {//否则进行修改
            schedule.setUpdateTime(new Date());//设置更新时间
            schedule.setIsDeleted(0);//设置是否逻辑删除
            //设置查询出来数据id值   有id值才会进行修改,否则会添加导致重复添加报错
            schedule.setId(findSchedule.getId());
            scheduleRepository.save(schedule);
        }


    }

    //分页查询排班信息带条件
    @Override
    public Page<Schedule> selectPage(int page, int limit, ScheduleQueryVo scheduleQueryVo) {
        Sort sort = Sort.by(Sort.Direction.DESC, "createTime");//按照创建时间进行降序
        //0为第一页 设置分页信息
        Pageable pageable = PageRequest.of(page-1, limit, sort);

        //创建一个schedule对象
        Schedule schedule = new Schedule();
        //将scheduleQueryVo中的属性的值复制给schedule
        BeanUtils.copyProperties(scheduleQueryVo, schedule);
        schedule.setIsDeleted(0);//设置删除状态为0

        //创建匹配器，即如何使用查询条件
        ExampleMatcher matcher = ExampleMatcher.matching() //构建对象
                .withStringMatcher(ExampleMatcher.StringMatcher.CONTAINING) //改变默认字符串匹配方式：模糊查询
                .withIgnoreCase(true); //改变默认大小写忽略方式：忽略大小写

        //创建实例
        Example<Schedule> example = Example.of(schedule, matcher);
        Page<Schedule> pages = scheduleRepository.findAll(example, pageable);
        return pages;
    }

    //删除排班信息
    @Override
    public void scheduleRemove(String hoscode, String hosScheduleId) {
        //先从数据库中查询是否存在该条记录
        Schedule schedule = scheduleRepository.getScheduleByHoscodeAndHosScheduleId(hoscode, hosScheduleId);
        if(schedule!=null){//存在,执行删除操作
            scheduleRepository.delete(schedule);
        }//如果不存在,则不执行任何操作
    }

    //根据医院编号 和 科室编号 ，查询排班规则数据
    @Override
    public Map<String, Object> getRuleSchedule(long page, long limit, String hoscode, String depcode) {
        //List<Schedule> scheduleList =   scheduleRepository.getScheduleByHoscodeAndDepcode(hoscode,depcode);
        //1 封装查询条件:根据医院编号 和 科室编号 查询
        Criteria criteria = Criteria.where("hoscode").is(hoscode).and("depcode").is(depcode);

        //2 封装聚合查询部分:
        Aggregation agg = Aggregation.newAggregation(
                Aggregation.match(criteria),//2.1查询条件
                // 2.2根据工作日workDate期进行分组
                Aggregation.group("workDate").first("workDate").as("workDate")//分组字段,按照workDate字段分组
                //2.3.统计号源数量
                .count().as("docCount")//出诊医生数量
                //2.4 求和操作,号源数量
                .sum("reservedNumber").as("reservedNumber")//可预约数量
                .sum("availableNumber").as("availableNumber"),//剩余预约数量
                //2.5排序
                Aggregation.sort(Sort.Direction.ASC,"workDate"),//按照workDate字段升序
                //2.6 分页
                Aggregation.skip((page-1)*limit),
                Aggregation.limit(limit)
        );
        //调用方法，最终执行
        //参数一: Aggregation 对象:封装的聚合条件
        //参数二:Schedule.class  MongoDB中集合(表)对应的实体类
        //参数三:返回结果的实体类
        AggregationResults<BookingScheduleRuleVo> aggregate = mongoTemplate.aggregate(agg,
                Schedule.class, BookingScheduleRuleVo.class);
        //获取最终查询到的数据
        List<BookingScheduleRuleVo> bookingScheduleRuleVoList = aggregate.getMappedResults();
        //遍历查到的数据 设置 日期所对应周几
        for (BookingScheduleRuleVo bookingScheduleRuleVo : bookingScheduleRuleVoList) {
            Date workDate = bookingScheduleRuleVo.getWorkDate();//获取workDate
            String dayOfWeek = this.getDayOfWeek(new DateTime(workDate));//获取workDate所对应的星期
            bookingScheduleRuleVo.setDayOfWeek(dayOfWeek);//将星期封装进bookingScheduleRuleVo对象中
        }
        //分组查询的总记录数 : 获取查询有多少调记录
        Aggregation aggregation = Aggregation.newAggregation(
                Aggregation.match(criteria),
                Aggregation.group("workDate")
        );
        AggregationResults<BookingScheduleRuleVo> totalAggResults = mongoTemplate.aggregate(aggregation, Schedule.class, BookingScheduleRuleVo.class);
        int total = totalAggResults.getMappedResults().size();//返回查到的集合的长度,就是总记录数

        //设置最终数据，进行返回
        Map<String, Object> result = new HashMap<>();
        result.put("bookingScheduleRuleList",bookingScheduleRuleVoList);//设置聚合查询到的数据
        result.put("total",total);//设置总记录数
        //获取医院名称
        String hosName = hospitalService.getHospName(hoscode);
        //创建map集合 并把hosName 添加进map集合 (符合前端代码的要求)
        HashMap<String, Object> map = new HashMap<>();
        map.put("hosName",hosName);
        result.put("baseMap",map);
        return result;

    }
    //根据医院编号 、科室编号和工作日期，查询排班详细信息
    @Override
    public List<Schedule> getDetailSchedule(String hoscode, String depcode, String workDate) {
       List<Schedule> scheduleList = scheduleRepository.findScheduleByHoscodeAndDepcodeAndWorkDate(hoscode,depcode,new DateTime(workDate));

       //使用流的方式遍历集合
        scheduleList.stream().forEach(item->{
            this.packageSchedule(item);//封装排班详情其他值 医院名称、科室名称、日期对应星期
        });
       return scheduleList;
    }
    //封装排班详情其他值 医院名称、科室名称、日期对应星期
    private void packageSchedule(Schedule schedule) {
        //设置医院名称
        schedule.getParam().put("hosname",hospitalService.getHospName(schedule.getHoscode()));
        //设置科室名称
        schedule.getParam().put("depname",
                departmentService.getDepName(schedule.getHoscode(),schedule.getDepcode()));
        //设置日期对应星期
        schedule.getParam().put("dayOfWeek",this.getDayOfWeek(new DateTime(schedule.getWorkDate())));
    }

    /**
     * 根据日期获取周几数据
     * @param dateTime
     * @return
     */
    private String getDayOfWeek(DateTime dateTime) {
        String dayOfWeek = "";
        switch (dateTime.getDayOfWeek()) {
            case DateTimeConstants.SUNDAY:
                dayOfWeek = "周日";
                break;
            case DateTimeConstants.MONDAY:
                dayOfWeek = "周一";
                break;
            case DateTimeConstants.TUESDAY:
                dayOfWeek = "周二";
                break;
            case DateTimeConstants.WEDNESDAY:
                dayOfWeek = "周三";
                break;
            case DateTimeConstants.THURSDAY:
                dayOfWeek = "周四";
                break;
            case DateTimeConstants.FRIDAY:
                dayOfWeek = "周五";
                break;
            case DateTimeConstants.SATURDAY:
                dayOfWeek = "周六";
            default:
                break;
        }
        return dayOfWeek;
    }
}
